﻿using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Loki;
using Loki.Commands;
using Loki.UI;
using Luna.Model.Project;

namespace Luna.UI.Project
{
    public class ProjectDetailViewModel : LunaViewModel
    {
        #region  CurrentProject
        private static PropertyChangedEventArgs _CurrentProjectChangeArgs = ObservableHelper.CreateArgs<ProjectDetailViewModel>(x => x.CurrentProject);

        private IEditableProject _CurrentProject;

        public IEditableProject CurrentProject
        {
            get
            {
                return _CurrentProject;
            }

            set
            {
                if (value != _CurrentProject)
                {
                    _CurrentProject = value;
                    NotifyChangedAndDirty(_CurrentProjectChangeArgs);
                }
            }
        }
        #endregion

        #region Editable
        #region  Code
        private static PropertyChangedEventArgs _CodeChangeArgs = ObservableHelper.CreateArgs<ProjectDetailViewModel>(x => x.Code);

        private string _Code;

        public string Code
        {
            get
            {
                return _Code;
            }

            set
            {
                if (value != _Code)
                {
                    _Code = value;
                    NotifyChangedAndDirty(_CodeChangeArgs);
                }
            }
        }
        #endregion

        #region  Description
        private static PropertyChangedEventArgs _DescriptionChangeArgs = ObservableHelper.CreateArgs<ProjectDetailViewModel>(x => x.Description);

        private string _Description;

        public string Description
        {
            get
            {
                return _Description;
            }

            set
            {
                if (value != _Description)
                {
                    _Description = value;
                    NotifyChangedAndDirty(_DescriptionChangeArgs);
                }
            }
        }
        #endregion

        #region  Name
        private static PropertyChangedEventArgs _NameChangeArgs = ObservableHelper.CreateArgs<ProjectDetailViewModel>(x => x.Name);

        private string _Name;

        public string Name
        {
            get
            {
                return _Name;
            }

            set
            {
                if (value != _Name)
                {
                    _Name = value;
                    NotifyChangedAndDirty(_NameChangeArgs);
                }
            }
        }
        #endregion

        #region  Budget
        private static PropertyChangedEventArgs _BudgetChangeArgs = ObservableHelper.CreateArgs<ProjectDetailViewModel>(x => x.Budget);

        private decimal? _Budget;

        public decimal? Budget
        {
            get
            {
                return _Budget;
            }

            set
            {
                if (value != _Budget)
                {
                    _Budget = value;
                    NotifyChangedAndDirty(_BudgetChangeArgs);
                }
            }
        }
        #endregion

        #region  EndDate
        private static PropertyChangedEventArgs _EndDateChangeArgs = ObservableHelper.CreateArgs<ProjectDetailViewModel>(x => x.EndDate);

        private DateTime? _EndDate;

        public DateTime? EndDate
        {
            get
            {
                return _EndDate;
            }

            set
            {
                if (value != _EndDate)
                {
                    _EndDate = value;
                    NotifyChangedAndDirty(_EndDateChangeArgs);
                }
            }
        }
        #endregion

        #region  StartDate
        private static PropertyChangedEventArgs _StartDateChangeArgs = ObservableHelper.CreateArgs<ProjectDetailViewModel>(x => x.StartDate);

        private DateTime? _StartDate;

        public DateTime? StartDate
        {
            get
            {
                return _StartDate;
            }

            set
            {
                if (value != _StartDate)
                {
                    _StartDate = value;
                    NotifyChangedAndDirty(_StartDateChangeArgs);
                }
            }
        }
        #endregion
        #endregion

        #region Computed

        #region  AllocatedPercentage
        private static PropertyChangedEventArgs _AllocatedPercentageChangeArgs = ObservableHelper.CreateArgs<ProjectDetailViewModel>(x => x.AllocatedPercentage);

        private decimal? _AllocatedPercentage;

        public decimal? AllocatedPercentage
        {
            get
            {
                return _AllocatedPercentage;
            }

            set
            {
                if (value != _AllocatedPercentage)
                {
                    _AllocatedPercentage = value;
                    NotifyChanged(_AllocatedPercentageChangeArgs);
                }
            }
        }
        #endregion

        #region  Remain
        private static PropertyChangedEventArgs _RemainChangeArgs = ObservableHelper.CreateArgs<ProjectDetailViewModel>(x => x.Remain);

        private decimal? _Remain;

        public decimal? Remain
        {
            get
            {
                return _Remain;
            }

            set
            {
                if (value != _Remain)
                {
                    _Remain = value;
                    NotifyChanged(_RemainChangeArgs);
                }
            }
        }
        #endregion

        #region  ProjectAdvance
        private static PropertyChangedEventArgs _ProjectAdvanceChangeArgs = ObservableHelper.CreateArgs<ProjectDetailViewModel>(x => x.ProjectAdvance);

        private decimal? _ProjectAdvance;

        public decimal? ProjectAdvance
        {
            get
            {
                return _ProjectAdvance;
            }

            set
            {
                if (value != _ProjectAdvance)
                {
                    _ProjectAdvance = value;
                    NotifyChanged(_ProjectAdvanceChangeArgs);
                }
            }
        }
        #endregion
        #endregion

        public LokiCollection<DisplayableTask> Tasks { get; private set; }

        public ProjectDetailViewModel()
        {
            Tasks = CreateTrackedCollection<DisplayableTask>();

            Init += ProjectDetailViewModel_Init;
            Load += ProjectDetailViewModel_Load;
        }

        private void ProjectDetailViewModel_Load(object P_Sender, EventArgs e)
        {
            LoadValues(CurrentProject);
        }

        private void ProjectDetailViewModel_Init(object P_Sender, EventArgs e)
        {
            Commands.Handle(ApplicationCommands.SAVE, Application_Save_CanExecute, Application_Save_Execute);

            this.WatchChange(this, x => x.Budget, v => v.Budget_Changed);
            this.WatchCollectionChange(Tasks, v => v.Tasks_CollectionChanged);
            Tasks.ItemChanged += Tasks_ItemChanged;

            this.WatchChange(this, x => x.StartDate, v => v.StartDate_Changed);
            this.WatchChange(this, x => x.EndDate, v => v.EndDate_Changed);
        }

        private void Tasks_ItemChanged(object P_Sender, ItemChangedEventArgs<DisplayableTask> e)
        {
            ComputeAllocation();
        }

        private void Budget_Changed(object P_Sender, PropertyChangedEventArgs e)
        {
            ComputeAllocation();
        }

        private void Tasks_CollectionChanged(object P_Sender, NotifyCollectionChangedEventArgs e)
        {
            ComputeAllocation();
        }

        private void StartDate_Changed(object P_Sender, PropertyChangedEventArgs e)
        {
            ComputeProgress();
        }

        private void EndDate_Changed(object P_Sender, PropertyChangedEventArgs e)
        {
            ComputeProgress();
        }

        private void ComputeProgress()
        {
            if (StartDate.HasValue && EndDate.HasValue)
            {
                if (StartDate.Value >= DateTime.Today)
                {
                    ProjectAdvance = 0;
                }
                else if (EndDate.Value < DateTime.Today || EndDate.Value == StartDate.Value)
                {
                    ProjectAdvance = 100;
                }
                else
                {
                    TimeSpan L_ProjectDays = EndDate.Value - StartDate.Value;
                    TimeSpan L_CurrentDays = DateTime.Today - StartDate.Value;
                    ProjectAdvance = Convert.ToDecimal(L_CurrentDays.TotalDays / L_ProjectDays.TotalDays) * 100;
                }
            }
            else
            {
                ProjectAdvance = null;
            }
        }

        private void ComputeAllocation()
        {
            decimal L_AllocatedCurrent = Tasks.Sum(x => x.Budget ?? 0);
            if (Budget.HasValue && Budget > 0)
            {
                AllocatedPercentage = L_AllocatedCurrent / Budget.Value * 100;
                if (L_AllocatedCurrent <= Budget.Value)
                {
                    Remain = Budget - L_AllocatedCurrent;
                }
                else
                {
                    Remain = 0;
                }
            }
            else
            {
                AllocatedPercentage = null;
                Remain = null;
            }
        }

        #region Application save
        private void Application_Save_CanExecute(object P_Sender, CanExecuteCommandEventArgs e)
        {
            e.CanExecute |= IsDirty;
        }

        private void Application_Save_Execute(object P_Sender, CommandEventArgs e)
        {
            RequiresValidation();
            if (IsValid)
            {
                try
                {
                    CollectValues(CurrentProject);
                    ApplicationModel.Project.Save(CurrentProject);

                    // contre validation
                    CurrentProject = ApplicationModel.Project.Projects[CurrentProject.ID].EditableVersion;
                    LoadValues(CurrentProject);

                    AcceptChanges();
                    UserMessage(Messages.UIMessages.SAVE_OK);
                }
                catch (Exception Ex)
                {
                    HandleException(Ex);
                }
            }
        }
        #endregion

        protected override bool ValidateData(ValidationMode P_Mode)
        {
            bool L_IsValid = !string.IsNullOrWhiteSpace(Code)
                            && !string.IsNullOrWhiteSpace(Name)
                            && Budget.HasValue
                            && EndDate.HasValue
                            && StartDate.HasValue;

            foreach (var L_Task in Tasks)
            {
                L_IsValid &= !string.IsNullOrEmpty(L_Task.Code);
                L_IsValid &= !string.IsNullOrEmpty(L_Task.Name);
                L_IsValid &= L_Task.EndDate.HasValue;
                L_IsValid &= L_Task.StartDate.HasValue;
                L_IsValid &= L_Task.Budget.HasValue;
            }

            return L_IsValid;
        }

        private void CollectValues(IEditableProject P_ToSave)
        {
            CurrentProject.Code = Code;
            CurrentProject.Name = Name;
            CurrentProject.Description = Description;
            CurrentProject.Budget = Budget.Value;
            CurrentProject.End = EndDate.Value;
            CurrentProject.Start = StartDate.Value;

            foreach (var L_DTask in Tasks.RemovedItems)
            {
                IProjectTask L_Task = P_ToSave.Tasks.FirstOrDefault(x => x.ID == L_DTask.ID);

                if (L_Task == null)
                {
                    P_ToSave.Remove(L_Task);
                }
            }

            foreach (var L_DTask in Tasks.Where(x => x.IsDirty))
            {
                IProjectTask L_Task = P_ToSave.Tasks.FirstOrDefault(x => x.ID == L_DTask.ID);
                if (L_Task == null)
                {
                    L_Task = ApplicationModel.Project.Create<IProjectTask>();
                }

                L_Task.Code = L_DTask.Code;
                L_Task.Name = L_DTask.Name;
                L_Task.Budget = L_DTask.Budget.Value;
                L_Task.Start = L_DTask.StartDate.Value;
                L_Task.End = L_DTask.EndDate.Value;

                P_ToSave.Add(L_Task);
            }
        }

        private void LoadValues(IProject P_ToLoad)
        {
            Tasks.BeginInit();
            Tasks.Clear();

            Code = CurrentProject.Code;
            Name = CurrentProject.Name;
            Description = CurrentProject.Description;
            Budget = CurrentProject.Budget == 0 ? null : (decimal?)CurrentProject.Budget;
            EndDate = CurrentProject.End == DateTime.MinValue ? null : (DateTime?)CurrentProject.End;
            StartDate = CurrentProject.Start == DateTime.MinValue ? null : (DateTime?)CurrentProject.Start;

            foreach (var L_Task in P_ToLoad.Tasks)
            {
                DisplayableTask L_DTask = new DisplayableTask();
                L_DTask.BeginInit();
                L_DTask.ID = L_Task.ID;
                L_DTask.Code = L_Task.Code;
                L_DTask.Name = L_Task.Name;
                L_DTask.Budget = L_Task.Budget;
                L_DTask.StartDate = L_Task.Start;
                L_DTask.EndDate = L_Task.End;
                L_DTask.EndInit();

                Tasks.Add(L_DTask);
            }

            Tasks.EndInit();

            ComputeAllocation();
            ComputeProgress();
        }
    }
}
